/*
 * Copyright 2017-2020 Amazon.com, Inc. or its affiliates. All Rights Reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License").
 * You may not use this file except in compliance with the License.
 * A copy of the License is located at
 *
 *     http://aws.amazon.com/apache2.0/
 *
 * or in the "license" file accompanying this file. This file is distributed
 * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
 * express or implied. See the License for the specific language governing
 * permissions and limitations under the License.
 */

#ifndef AACE_ALEXA_ALEXA_CONFIGURATION_H
#define AACE_ALEXA_ALEXA_CONFIGURATION_H

#include <fstream>
#include <iostream>
#include <string>
#include <vector>
#include <sstream>
#include <memory>
#include <chrono>

#include "AACE/Core/EngineConfiguration.h"
#include "AlexaEngineInterfaces.h"

/** @file */

namespace aace {
namespace alexa {
namespace config {

/**
 * A factory interface for creating Alexa configuration objects
 */
class AlexaConfiguration {
public:

    /**
     * Factory method used to programmatically generate device info configuration data.
     * The data generated by this method is equivalent to providing the following JSON
     * values in a configuration file:
     *
     * @code{.json}
     * {
     *   "deviceInfo":
     *   {
     *     "deviceSerialNumber": "<DEVICE_SERIAL_NUMBER>"
     *     "clientId": "<CLIENT_ID>",
     *     "productId": "<PRODUCT_ID>"
     *     "manufacturerName": "<MANUFACTURER_NAME>"
     *     "description": "<DESCRIPTION>"
     *   }
     * }
     * @endcode
     *
     * @param [in] deviceSerialNumber The device serial number used to authorize the client with AVS
     * @param [in] clientId The client ID used to authorize the client with AVS
     * @param [in] productId The product ID used to authorize the client with AVS
     * @param [in] manufacturerName The manufacturer name of the product
     * @param [in] description The description of the product
     */
    static std::shared_ptr<aace::core::config::EngineConfiguration> createDeviceInfoConfig( const std::string& deviceSerialNumber, const std::string& clientId, const std::string& productId, const std::string& manufacturerName, const std::string& description  );
    
    /**
     * Factory method used to programmatically generate alerts configuration data.
     * The data generated by this method is equivalent to providing the following JSON
     * values in a configuration file:
     *
     * @code{.json}
     * {
     *   "alertsCapabilityAgent":
     *   {
     *     "databaseFilePath": "<SQLITE_DATABASE_FILE_PATH>"
     *   }
     * }
     * @endcode
     *
     * @param [in] databaseFilePath The file path to the SQLite database used to store persistent alerts data.
     * The database will be created on initialization if it does not already exist.
     */
    static std::shared_ptr<aace::core::config::EngineConfiguration> createAlertsConfig( const std::string& databaseFilePath );
    
    /**
     * Factory method used to programmatically generate notifications configuration data.
     * The data generated by this method is equivalent to providing the following JSON
     * values in a configuration file:
     *
     * @code{.json}
     * {
     *   "notifications":
     *   {
     *     "databaseFilePath": "<SQLITE_DATABASE_FILE_PATH>"
     *   }
     * }
     * @endcode
     *
     * @param [in] databaseFilePath The file path to the SQLite database used to store persistent notifications data.
     * The database will be created on initialization if it does not already exist.
     */
    static std::shared_ptr<aace::core::config::EngineConfiguration> createNotificationsConfig( const std::string& databaseFilePath );
    
    /**
     * Factory method used to programmatically generate certified sender configuration data.
     * The data generated by this method is equivalent to providing the following JSON
     * values in a configuration file:
     *
     * @code{.json}
     * {
     *   "certifiedSender":
     *   {
     *     "databaseFilePath": "<SQLITE_DATABASE_FILE_PATH>"
     *   }
     * }
     * @endcode
     *
     * @param [in] databaseFilePath The file path to the SQLite database used to store persistent certified sender data.
     * The database will be created on initialization if it does not already exist.
     */
    static std::shared_ptr<aace::core::config::EngineConfiguration> createCertifiedSenderConfig( const std::string& databaseFilePath );

    /**
     * Factory method used to programmatically generate capabilities delegate configuration data.
     * The data generated by this method is equivalent to providing the following JSON
     * values in a configuration file:
     *
     * @code{.json}
     * {
     *   "capabilitiesDelegate":
     *   {
     *     "databaseFilePath": "<SQLITE_DATABASE_FILE_PATH>"
     *   }
     * }
     * @endcode
     *
     * @param [in] databaseFilePath The file path to the SQLite database used to store device capabilities.
     * The database will be created on initialization if it does not already exist.
     */
    static std::shared_ptr<aace::core::config::EngineConfiguration> createCapabilitiesDelegateConfig( const std::string& databaseFilePath );
    /**
     * Factory method used to programmatically generate CURL configuration data.
     *
     * The 'libCurlUtils' sub-component of the global configuration supports the following options:
     * - CURLOPT_CAPATH If present, specifies a value for the libcurl property CURLOPT_CAPATH.
     * - CURLOPT_INTERFACE if present, specifies a value for the libcurl property CURLOPT_INTERFACE.
     *
     * The data generated by this method is equivalent to providing the following JSON
     * values in a configuration file:
     *
     * @code{.json}
     * {
     *     "libcurlUtils" : {
     *         "CURLOPT_CAPATH" : "<CA_CERTIFICATES_FILE_PATH>"
     *         "CURLOPT_INTERFACE" : "<NETWORK_INTERFACE_NAME>"
     *     }
     * }
     * @endcode
     *
     * @param [in] certsPath The file path to the directory holding CA certificates
     * @param [in] iface The specific network interface to use. This can be a network interface name, an IP address or a host name.
     * Default to the system's primary network interface.
     */
    static std::shared_ptr<aace::core::config::EngineConfiguration> createCurlConfig( const std::string &certsPath, const std::string &iface = "" );

    /**
     * THIS FUNCTION IS DEPRECATED AND WILL BE REMOVED IN THE FOLLOWING RELEASE
     *
     * Factory method used to programmatically generate settings configuration data.
     * The data generated by this method is equivalent to providing the following JSON
     * values in a configuration file:
     *
     * @code{.json}
     * {
     *   "settings": {
     *     "databaseFilePath": "<SQLITE_DATABASE_FILE_PATH>",
     *     "defaultAVSClientSettings": {
     *        "locale": "<LOCALE>"
     *     }
     *   }
     * }
     * @endcode
     *
     * @param [in] databaseFilePath The file path to the SQLite database used to store persistent settings data.
     * The database will be created on initialization if it does not already exist.
     * @param [in] locale The current locale setting on the client. Default to "en-US".
     */
    static std::shared_ptr<aace::core::config::EngineConfiguration> createSettingsConfig( const std::string& databaseFilePath, const std::string& locale = "en-US" );

    /**
     * Factory method used to programmatically generate device settings configuration data.
     * The data generated by this method is equivalent to providing the following JSON
     * values in a configuration file:
     *
     * @code{.json}
     * {
     *   "deviceSettings": {
     *     "databaseFilePath": "<SQLITE_DATABASE_FILE_PATH>",
     *     "locales": [<LIST_OF_LOCALE_STRINGS>],
     *     "defaultLocale": "<DEFAULT_LOCALE>",
     *     "localeCombinations": [[<LOCAL_STRING_PAIR>]],
     *     "defaultTimezone": "<TIMEZONE>"        
     *   }
     * }
     * @endcode
     *
     * @param [in] databaseFilePath The file path to the SQLite database used to store persistent settings data.
     * The database will be created on initialization if it does not already exist.
     * @param [in] locales The current list of supportable locales on the client. Defaults to ["en-US","en-GB","de-DE",
     *          "en-IN","en-CA","ja-JP","en-AU","fr-FR","it-IT","es-ES","es-MX","fr-CA","es-US", "hi-IN", "pt-BR"].
     * @param [in] defaultLocale The current default locale setting on the client. Defaults to "en-US".
     * @param [in] defaultTimezone The current default timezone setting on the client. Defaults to "America/Vancouver".
     */
    static std::shared_ptr<aace::core::config::EngineConfiguration> createDeviceSettingsConfig( const std::string& databaseFilePath, 
        const std::vector<std::string>& locales = {"en-US","en-GB","de-DE","en-IN","en-CA","ja-JP","en-AU","fr-FR","it-IT","es-ES","es-MX","fr-CA",
                    "es-US", "hi-IN", "pt-BR"}, const std::string& defaultLocale = "en-US",
        const std::string& defaultTimezone = "America/Vancouver" );

    /**
     * Factory method used to programmatically generate misc storage configuration data.
     * The data generated by this method is equivalent to providing the following JSON
     * values in a configuration file:
     *
     * @code{.json}
     * {
     *   "miscDatabase":
     *   {
     *     "databaseFilePath": "<SQLITE_DATABASE_FILE_PATH>",
     *   }
     * }
     * @endcode
     *
     * @param [in] databaseFilePath The file path to the SQLite database used to store persistent misc storage data.
     * The database will be created on initialization if it does not already exist.
     */
    static std::shared_ptr<aace::core::config::EngineConfiguration> createMiscStorageConfig( const std::string& databaseFilePath );

    /**
     * Factory method used to programmatically generate system configuration data.
     * The data generated by this method is equivalent to providing the following JSON
     * values in a configuration file:
     *
     * @code{.json}
     * {
     *   "aace.alexa": {
     *      "system": {
     *          "firmwareVersion": "<FIRMWARE_VERSION>"
     *      }
     *   }
     * }
     * @endcode
     *
     * @param [in] firmwareVersion The firmware version of the client device
     */
    static std::shared_ptr<aace::core::config::EngineConfiguration> createSystemConfig( uint32_t firmwareVersion );

    /**
     * Factory method used to programmatically generate encoder configuration data.
     * The data generated by this method is equivalent to providing the following JSON
     * values in a configuration file:
     *
     * @code{.json}
     * {
     *   "aace.alexa": {
     *      "speechRecognizer": {
     *          "encoder": {
     *               "name": "<ENCODER_NAME>"
     *          }
     *      }
     *   }
     * }
     * @endcode
     *
     * @param [in] encoderName The encoder codec name to be used
     */
    static std::shared_ptr<aace::core::config::EngineConfiguration> createSpeechRecognizerConfig( const std::string& encoderName );

    /**
     * enum specifying the configurable TemplateRuntime timeout.
     */
    enum class TemplateRuntimeTimeoutType {
        /**
         *  Display Card timeout in ms when Alexa completes TTS
         */
        DISPLAY_CARD_TTS_FINISHED_TIMEOUT,

        /**
         *  Display Card timeout in ms when AudioPlayback Completes.
         */
        DISPLAY_CARD_AUDIO_PLAYBACK_FINISHED_TIMEOUT,

        /**
         *  Display Card timeout in ms when AudioPlayback Stopped or Paused.
         */
        DISPLAY_CARD_AUDIO_PLAYBACK_STOPPED_PAUSED_TIMEOUT,

    };

    /**
    * Identifies a Template Runtime configuration with a type and value pair
    */
    using TemplateRuntimeTimeout = std::pair<TemplateRuntimeTimeoutType, std::chrono::milliseconds>;

    /**
     * Factory method used to programmatically generate template runtime configuration data.
     * This is an optional configuration. Following are the accepted keys and their description.
     * - displayCardTTSFinishedTimeout If present, specifies the values in milli seconds to control the timeout of display card at the end of Alexa TTS.
     * - displayCardAudioPlaybackFinishedTimeout If present, specifies the values in milli seconds to control the timeout of display card at the FINISHED state of AudioPlayback.
     * - displayCardAudioPlaybackStoppedPausedTimeout If present, specifies the values in milli seconds to control the timeout of display card at STOP or PAUSE state of AudioPlayback.
     * The data generated by this method is equivalent to providing the following JSON
     * values in a configuration:
     *
     * @code{.json}
     * {
     *   "templateRuntimeCapabilityAgent": {
     *      "displayCardTTSFinishedTimeout": <TIMEOUT_IN_MS>,
     *      "displayCardAudioPlaybackFinishedTimeout": <TIMEOUT_IN_MS>,
     *      "displayCardAudioPlaybackStoppedPausedTimeout": <TIMEOUT_IN_MS>
     *   }
     * }
     * @endcode
     *
     * @param [in] timeoutList A list of @c TemplateRuntimeTimeout type and value pairs
     *
     */
    static std::shared_ptr<aace::core::config::EngineConfiguration> createTemplateRuntimeTimeoutConfig( const std::vector<TemplateRuntimeTimeout>& timeoutList );

    /**
     * Factory method used to programmatically generate external media player configuration data.
     * The data generated by this method is equivalent to providing the following JSON
     * values in a configuration file:
     *
     * @code{.json}
     * {
     *   "aace.alexa": {
     *      "externalMediaPlayer": {
     *          "agent": "<agent>"
     *      }
     *   }
     * }
     * @endcode
     *
     * @param [in] agent The external media player agent
     */
    static std::shared_ptr<aace::core::config::EngineConfiguration> createExternalMediaPlayerConfig( const std::string& agent );

    /**
     * Describes the equalizer bands supported by Alexa. The platform implementation may support a subset of these.
     * 
     * @sa aace::alexa::EqualizerControllerEngineInterface::EqualizerBand
     */
    using EqualizerBand = aace::alexa::EqualizerControllerEngineInterface::EqualizerBand;

    /**
     * Describes the level of gain of a particular equalizer band as an integer dB value. This is an
     * @c aace::alexa::EqualizerController::EqualizerBand and @c int pair.
     * 
     * @sa aace::alexa::EqualizerControllerEngineInterface::EqualizerBandLevel
     */ 
    using EqualizerBandLevel = aace::alexa::EqualizerControllerEngineInterface::EqualizerBandLevel;

    /**
     * Factory method used to programmatically generate equalizer controller configuration data. This is an optional 
     * configuration, and default settings will be used if configuration is not provided. This method produces 
     * configuration data according to the JSON structure in the sample below.
     *
     * @code{.json}
     *  "equalizer": {
     *      "bands": {
     *          "BASS": true,
     *          "MIDRANGE": false,
     *          "TREBLE": true
     *      },
     *      "defaultState": {
     *          "bands": {
     *              "BASS": 4,
     *              "TREBLE": -1
     *          }
     *      },
     *      "minLevel": -6,
     *      "maxLevel": 6
     *  }
     * @endcode
     *
     * The configuration branches are used as follows:
     *
     * @li equalizer.bands: Specifies which bands are supported by the device and will be enabled for control with 
     *  Alexa. Each child key is the name of an Alexa-supported band ("BASS", "MIDRANGE", or "TREBLE") and value is 
     *  whether the device supports the band. Only bands explicitly declared supported will be enabled in the SDK and 
     *  Alexa. Omitting this branch enables all bands by default.
     *
     * @li equalizer.defaultState: Describes the default or reset state of the equalizer. These settings are used to 
     *  reset the equalizer with Alexa such as by saying "Alexa, reset bass." If this branch or its child is omitted, 
     *  default values will be used.
     * @li equalizer.defaultState.bands: Defines the default gain level setting in dB for each supported equalizer 
     *  band. Each element key is the name of a supported band and value is a level (int) specifying the default gain 
     *  in dB. All of the supported bands must be provided once this branch is defined. All dB levels must obey the 
     *  limits declared in "equalizer.minLevel" and "equalizer.maxLevel". Omitting this branch uses the default 0dB for 
     *  each band.
     * 
     * @li equalizer.minLevel and equalizer.maxLevel: Integer values specifying the decibel level range on which Alexa 
     *  may operate for the supported bands. The device may support a different range internally, but Alexa will know 
     *  only about the limits declared here. Values should be specified as absolute amplitude gain in integer dB and
     *  scaled to the platform's internal range as necessary. If these values are omitted, the default range min -6dB 
     *  and max +6dB will be used.
     *
     * @param supportedBands A list of supported equalizer bands. Corresponds to the "equalizer.bands" config branch. 
     *        Only bands provided in the list will be enabled. Unspecified or empty @a supportedBands omits the config 
     *        branch. Nonempty @a supportedBands includes the branch and declares each band in the list with a value 
     *        "true".
     * @param minLevel The minimum gain level for the equalizer bands in integer dB. Corresponds to 
     *        "equalizer.minLevel". Unspecified @a minLevel uses the -6dB default.
     * @param maxLevel The maximum gain level for the equalizer bands in integer dB. Corresponds to 
     *        "equalizer.maxLevel". Unspecified @a maxLevel uses the +6dB default.
     * @param defaultBandLevels The default or reset state of the equalizer bands. Corresponds to the 
     *        "equalizer.defaultState.bands" config branch. Unspecified or empty @a defaultBandLevels omits the config 
     *        branch.
     */
    static std::shared_ptr<aace::core::config::EngineConfiguration> createEqualizerControllerConfig(
        const std::vector<EqualizerBand>& supportedBands = {},
        int minLevel = -6,
        int maxLevel = 6,
        const std::vector<EqualizerBandLevel>& defaultBandLevels = {} );

};

} // aace::alexa::config
} // aace::alexa
} // aace

#endif // AACE_ALEXA_ALEXA_CONFIGURATION_H
